

#include "cybertron/core/JsonReader.hpp"
#include "cybertron/core/UtilFile.hpp"
#include "cybertron/core/UtilPath.hpp"
#include "cybertron/core/Log.hpp"

CYBERTRON_BEGIN

const nlohmann::json JsonReader::smDummyNode;

CybertronCoreAPI bool JsonReader::loadString(
	nlohmann::json& jsonNode,
	const std::string& content)
{
	try
	{
		jsonNode = nlohmann::json::parse(content);
		return true;
	}
	catch (json::parse_error& e)
	{
		std::string s;
		size_t pos1 = (e.byte - 50 < 0 ? 0 : e.byte - 50);
		if (e.byte > 1)
			s += content.substr(pos1, e.byte - 1 - pos1);
		s += " [[[ " + content.substr(e.byte - 1, 1) + " ]]] ";
		if (e.byte < content.length())
			s += content.substr(e.byte);
		logError("Failed to parse json string near the [[[]]] marks: [%s]", s.c_str());
		logError("Error %d: %s", e.id, e.what());
		return false;
	}
	catch (...)
	{
		logError("Unknown errors when parsing json string [%s]", content.c_str());
		return false;
	}
}

CybertronCoreAPI bool JsonReader::loadFile(
	nlohmann::json& jsonNode,
	const std::string& filename)
{
	try
	{
		std::string fileContent = UtilFile::readUTF8StringFromFile(filename);
		return loadString(jsonNode, fileContent);
	}
	catch (...)
	{
		//logError("Failed to load json file [%s]", filename.c_str());
		return false;
	}
}

CybertronCoreAPI bool JsonReader::hasValue(const nlohmann::json & jsonNode, const std::string & key)
{
	nlohmann::json::const_iterator it = jsonNode.find(key);
	if (it == jsonNode.end())
	{
		return false;
	}
	return true;
}

CybertronCoreAPI const nlohmann::json& JsonReader::getChild(
	const nlohmann::json& jsonNode,
	const std::string& key)
{
	nlohmann::json::const_iterator it = jsonNode.find(key);
	if (it == jsonNode.end())
	{
		return smDummyNode;
	}

	return (*it);
}

CybertronCoreAPI std::string JsonReader::getString(
	const nlohmann::json& jsonNode)
{
	if (!jsonNode.is_string())
	{
		return std::string();
	}

	return jsonNode.get<std::string>();
}

CybertronCoreAPI std::string JsonReader::getString(
	const nlohmann::json& jsonNode,
	const std::string& key)
{
	nlohmann::json::const_iterator it = jsonNode.find(key);
	if (it == jsonNode.end())
	{
		return std::string();
	}

	if (!it->is_string())
	{
		return std::string();
	}

	return it->get<std::string>();
}

CybertronCoreAPI vec3 JsonReader::getVec3(
	const nlohmann::json& jsonNode)
{
	if (!jsonNode.is_array())
	{
		return vec3(0, 0, 0);
	}

	vec3 result(0, 0, 0);
	if (jsonNode.size() > 0)
	{
		result.x = (float)getDouble(jsonNode[0]);
	}
	if (jsonNode.size() > 1)
	{
		result.y = (float)getDouble(jsonNode[1]);
	}
	if (jsonNode.size() > 2)
	{
		result.z = (float)getDouble(jsonNode[2]);
	}

	return result;
}

CybertronCoreAPI dvec3 JsonReader::getVec3d(
	const nlohmann::json& jsonNode,
	const std::string& key)
{
	const nlohmann::json& jsonChild = getChild(jsonNode, key);

	return getVec3d(jsonChild);
}

CybertronCoreAPI dvec3 JsonReader::getVec3d(
	const nlohmann::json& jsonNode)
{
	if (!jsonNode.is_array())
	{
		return dvec3(0, 0, 0);
	}

	dvec3 result(0, 0, 0);
	if (jsonNode.size() > 0)
	{
		result.x = getDouble(jsonNode[0]);
	}
	if (jsonNode.size() > 1)
	{
		result.y = getDouble(jsonNode[1]);
	}
	if (jsonNode.size() > 2)
	{
		result.z = getDouble(jsonNode[2]);
	}

	return result;
}

CybertronCoreAPI vec3 JsonReader::getVec3(
	const nlohmann::json& jsonNode,
	const std::string& key)
{
	const nlohmann::json& jsonChild = getChild(jsonNode, key);

	return getVec3(jsonChild);
}

CybertronCoreAPI bool JsonReader::getBool(
	const nlohmann::json& jsonNode)
{
	if (!jsonNode.is_boolean())
	{
		return false;
	}

	return jsonNode.get<bool>();
}

CybertronCoreAPI bool JsonReader::getBool(
	const nlohmann::json& jsonNode,
	const std::string& key)
{
	nlohmann::json::const_iterator it = jsonNode.find(key);
	if (it == jsonNode.end())
	{
		return false;
	}

	if (!it->is_boolean())
	{
		return false;
	}

	return it->get<bool>();
}

CybertronCoreAPI std::string JsonReader::getFilename(
	const nlohmann::json& jsonNode,
	const std::string& rootPath,
	const std::string& key)
{
	std::string path = getString(jsonNode, key);
	if (path.size() == 0)
	{
		return path;
	}

	return UtilPath::combine(rootPath, path);
}

CybertronCoreAPI int JsonReader::getInt(
	const nlohmann::json& jsonNode)
{
	if (!jsonNode.is_number())
	{
		return 0;
	}

	return jsonNode.get<int>();
}

CybertronCoreAPI int JsonReader::getInt(
	const nlohmann::json& jsonNode,
	const std::string& key)
{
	nlohmann::json::const_iterator it = jsonNode.find(key);
	if (it == jsonNode.end())
	{
		return 0;
	}

	if (!it->is_number())
	{
		return 0;
	}

	return it->get<int>();
}
CybertronCoreAPI float JsonReader::getFloat(
	const nlohmann::json& jsonNode)
{
	if (!jsonNode.is_number())
	{
		return 0;
	}

	return jsonNode.get<float>();
}

CybertronCoreAPI float JsonReader::getFloat(
	const nlohmann::json& jsonNode,
	const std::string& key)
{
	nlohmann::json::const_iterator it = jsonNode.find(key);
	if (it == jsonNode.end())
	{
		return 0;
	}

	if (!it->is_number())
	{
		return 0;
	}

	return it->get<float>();
}
CybertronCoreAPI double JsonReader::getDouble(
	const nlohmann::json& jsonNode)
{
	if (!jsonNode.is_number())
	{
		return 0;
	}

	return jsonNode.get<double>();
}

CybertronCoreAPI double JsonReader::getDouble(
	const nlohmann::json& jsonNode,
	const std::string& key)
{
	nlohmann::json::const_iterator it = jsonNode.find(key);
	if (it == jsonNode.end())
	{
		return 0;
	}

	if (!it->is_number())
	{
		return 0;
	}

	return it->get<double>();
}

CybertronCoreAPI std::string JsonReader::toString(
	const nlohmann::json& jsonNode)
{
	return jsonNode.dump();
}

CybertronCoreAPI std::string JsonReader::toString(
	const nlohmann::json& jsonNode,
	const std::string& key)
{
	nlohmann::json::const_iterator it = jsonNode.find(key);
	if (it == jsonNode.end())
	{
		return std::string();
	}

	return toString(*it);
}

CYBERTRON_END
